写一个订阅模式,实现UniApp与H5之间的互相通信

您所在的位置:网站首页 uniAPP webview 通讯 写一个订阅模式,实现UniApp与H5之间的互相通信

写一个订阅模式,实现UniApp与H5之间的互相通信

2024-03-21 22:43| 来源: 网络整理| 查看: 265

首先,项目是混合app即uniapp做框架,内容八成是H5,所以通信就成了重中之重了

因为我也是第一次接触app端开发,而且uniapp开发不如vue熟悉,其中就带有很多疑惑点了

用uni开发打包成app的话,那还能用uni的方法嘛,比如uni.scan之类的 (因为之前我直接用uni开发的小程序打包成app,打开后页面一篇空白) 打包成app要那些步骤,怎么搞。

后来重新搭建uni项目后,发现我真的多虑了,很多时候时间一下答案就出来了

对于第一点回答,你尽管用uni的方法即可,人家内部会处理好这些的啦~ 打包app的话,安卓和ios都不一样的,下面介绍两种方式 第一种 去dcloud官网 -> 我的应用 -> 点击应用名称 -> Android证书 -> 然后点生成 如图

image.png

第二种 让后端给你搞一个自定义证书,比较麻烦,不过不用我们动手比较舒服(好像要用as去生成) 至于IOS 我这边刚开发,账号都没申请下来,等到那一天我再补充这个坑

其他坑末尾补充

到了通信这块了,虽然通信都是用uni.webview.js (下面统称wv.js) 来实现的,但是网上参次不齐的资料,写法多数有问题,不排除我的问题(我全抄了都出错),下面说说我的写法

因为是混合app,H5页面不止我一方,还有其他部门第三方等接入的,类似小程序接入H5页面一样,H5需要使用到wx.jssdk提供的方法例如扫码跳转等方法,因为不是在微信环境,所以sdk需要我们来写一份实现

实现过程

再该业务情景下,我们需要提供一个方法给第三方,让他调我们的方法,然后把方法结果传回去给他

使用 wv.js提供的postMessage作为桥梁,将通信发送给对方

uniapp页面处理如下

html: js: // 获取H5页面消息 async message(e) { let that = this; // 通过wv.js传回来的数据,是嵌套这么多层的... let eventParams = e?.detail?.data.length ? e?.detail?.data[0] : {}; // 这个做法是匹配后续的一个js服务,拿到对应的服务执行,然后把结果返回给H5 // 下面这两个参数是自定义的,1是参数 2是执行的方法名 let { params = null, type } = eventParams; // 从sdk.js中解构出对应的方法,没有匹配上就用sdk.js默认的defaultFn匹配下 let { [type]: func = sdk['defaultFn'] = sdk; // 默认服务为成功,type为方法名,返回给H5时候,H5可以知道是那个方法执行的 let serviceMsg = { status: 'success', type: type }; // 执行方法,如果方法报错则在catch中设置执行为失败状态 let res = await func(params).catch((err) => { serviceMsg.status = 'fail' serviceMsg = { ...serviceMsg, error: err } }) serviceMsg = { ...serviceMsg, ...res } // 开始执行,通知H5执行完成,把结果返回去 that.postH5Mes(serviceMsg); }, postH5Mes(msg) { // 如果当前没有webview实例,则重新赋值一次,该操作是避免H5一开始调用wv还未生成报ecalJS 未定义, 在页面初始化的时候也要赋值一下给wv,就是下面if里面的语句,我没写而已~ // #ifdef APP-PLUS if (!this.wv) { let currentWebview = this.$scope.$getAppWebview(); this.wv = currentWebview.children()[0]; } // 自定义方法getUNIMsg this.wv.evalJS("document.dispatchEvent(new CustomEvent('noticeH5',{detail: {msg:" + JSON.stringify(msg) + "}}))") // #endif },

sdk.js 页面

const scanCode = (obj = {}) => { let params = { scanType: ['qrCode'], onlyFromCamera: true, ...obj } return new Promise((resolve, reject) => { uni.scanCode({ ...params, success: function(res) { resolve(res) }, fail(err) { reject(err) } }); }) } export default { scanCode } H5 Sdk开发,sdk说实话就是一个类而已,里面封装了一系列的方法

如果你这个sdk是第三方通过script标签引入的话,可以考虑一下 将wv.js页面源码下载下来,放在这个类最前面

如果是直接给这个js文件给第三方用,则wv.js通过异步引入也可以

// 第一种 *** uni.webview.1.5.2.js *** xxxxxx *** uni.webview.1.5.2.js *** class SDK { } // 第二种 *** uni.webview.1.5.2.js *** xxxxxx *** uni.webview.1.5.2.js *** class SDK { constructor() { this.LoadUniJS() } LoadUniJS = async () => { return new Promise((resolve, reject) => { if (window.uni) { resolve(window.uni); } else { const script = document.createElement("script"); script.src = "https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"; script.type = "text/javascript"; script.async = true; script.onerror = reject; document.body.appendChild(script); script.onload = () => { resolve(window.uni); }; } }); }; }

下面写下第一种方式的

*** uni.webview.1.5.2.js *** xxxxxxxxxxx *** uni.webview.1.5.2.js *** class UniService { VALUE = null; // 调取uni服务后回来的值 UNI = window.uni; // 保存uni的值 Dep = {}; // 调度中心,状态变更通知更新 // 缓存,主要是进入h5页面立即要更新的话,用它缓存下来 等uniapp webView可通信后执行 // 因为一开始调用的时候,webview还没加载完 通信是不成功的 cache = []; constructor() { // 监听Uni向H5发送结果的回调 this._ListenDocument() // 监听可通信后的回调,主要用于cache缓存的执行 this._ListenUniLinkH5() } /** * 要调用uni的方法 * @type {*} 你sdk.js里面写的方法名 * @param {*} params 参数 */ subscribe = (type, params) => { if (!type) { throw new Error("_Subscribe需要接受一个type属性进行注册"); } // 默认从参数中解构出成功与失败的回调,待响应结果之后进行推送 let { success, fail, ...argsObj } = params; if (!argsObj) argsObj = {}; success = success && typeof success === "function" ? success : () => this.VALUE; fail = fail && typeof fail === "function" ? fail : () => this.VALUE; // 注册该类型的属性 !this.Dep[type] && (this.Dep[type] = []); // 默认0 是成功的回调 1是失败的回调 this.Dep[type].push(success); this.Dep[type].push(fail); this.SendToUni(type, argsObj); }; /** * 获取Uni传输过来的数据 * @param {*} type uni服务类型 * @param {*} params 参数 * @returns null */ receiveUniInfo = (e) => { this.VALUE = e.detail.msg || {}; let { status = "fail", type } = this.VALUE; if (!this.Dep[type]) return; if (iswStatus === "success") { this.Dep[type][0](this.VALUE); } if (iswStatus === "fail") { this.Dep[type][1](this.VALUE); } this.Dep[type] = []; }; /** * 通信:发送信息给uni * @param {*} type uni服务类型 * @param {*} params 参数 * @returns null */ SendToUni = (type, params) => { let serviceUni = this.UNI || window.uni; this.VALUE = null; serviceUni.postMessage({ data: { type, params, }, }); }; /** * 这个是如果一开始调用的话 则将它加入到缓存中 */ setInitCallBack = (cbArr) => { if(cbArr && Array.isArray(cbArr)) { cbArr.forEach(el => { if(typeof el === 'function') { this.cache.push(el) } }) } } /** * 通信:监听document getUNIMsg * @param {*} type uni服务类型 * @param {*} params 参数 * @returns null */ _ListenDocument = () => { document.addEventListener("noticeH5", this.receiveUniInfo, false); }; /** * 通信:监听 通信桥梁是否搭建完成 * @returns null */ _ListenUniLinkH5 = () => { document.addEventListener('UniAppJSBridgeReady', this._UniAppJSBridgeReady, false) } /** * 缓存钩子,有则取出来执行 * @returns null */ _UniAppJSBridgeReady = () => { if (this.cache.length) { this.cache.forEach(el=>{ el(); }) this.cache = []; } } } (function () { if (!window.UniService) { window.UniService = new UniService(); } })(window);

上述代码都有解析,这边做一个总结:

首先在uni页面开发好sdk.js,里面封装uni服务,例如uni.scan扫码服务,然后在webview页面监听H5的消息,接收消息的函数message中主要接收两个值,一个是sdk中的方法名,一个是传给这个方法的参数 在接收到H5的需求后,webview页面的message函数去sdk中提取出对应的方法执行,然后把执行完的数据通过postH5Mes中自定义的noticeH5去发送消息 H5中通过UniService中的receiveUniInfo 可以获取到uni发送过来的消息,再对应执行调度中心dep中的回调

以下是注意事项

subscribe该方法是订阅uni sdk.js中方法的函数,在该方法中,我会缓存成功和失败的回调到调度中心dep,等uni服务执行完后,我会取出该次服务下对应的成功失败回调,

let { success, fail, ...argsObj } = params;

!this.Dep[type] && (this.Dep[type] = []);

使用方法如下:

// 因为我是挂在window下,所以可以通过window去引用了 UniService.subscribe('sdk中的方法名', { success:(e)=>{ }, fail:(e)=>{ } })

如果有什么不懂可以评论,我也是刚接触,文字功底很浅,有什么建议也可以写给弟弟听这是刚完成的第一版,后续有更新会更新



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3